`include "defines.svh"
`include "CP0Defines.svh"
module BranchSolve (
    input logic clk,
    input logic rst,
    input logic [31:0]EXE_PC,
    input logic [31:0]PREIF_PC,
    input logic [31:0]EXE_Instr,
    input logic EXE_IsBranch,
    input logic EXE_Immjump,
    input logic [31:0]EXE_Imm_32,
    input logic [31:0]EXE_Rs_Data,
    input logic [31:0]EXE_Rt_Data,
    input logic ID_EXE_Stall,
    input PredictResult EXE_PredictResult,//分支预测情况


    output FactResult EXE_FactResult,//分支实际情况
    output logic EXE_IsTaken,        //分支实际发生
    output logic EXE_PF_FlushAll,
    output logic EXE_Prediction_Failed,//表示预测失败
    output logic [31:0] EXE_Correction_Vector//用于校正的地�?向量
);

//BranchSolve
    logic Branch_IsTaken;
    logic [31:0]Branch_Target;
    logic [2:0]Branch_Type;

    logic Prediction_Success;

    always_comb begin
        if(EXE_Immjump)begin
            if(EXE_Instr[31:26] == `R_type)begin//JR
                Branch_Target = EXE_Rs_Data;
                Prediction_Success = (EXE_PredictResult.Target == EXE_Rs_Data);

                Branch_IsTaken = 1'b1;
                if(EXE_Instr[5:0] == `jalr)begin
                    if(EXE_Instr[15:11] == 5'b11111)
                        Branch_Type = `BranchIsCall;
                    else if(EXE_Instr[25:21] == 5'b11111)
                        Branch_Type = `BranchIsReturn;
                    else    
                        Branch_Type = `BranchIsJump;
                end
                else begin
                    if(EXE_Instr[25:21] == 5'b11111)
                        Branch_Type = `BranchIsReturn;
                    else
                        Branch_Type = `BranchIsJump;
                end
            end
            else begin//J
                Branch_Target = {EXE_PC[31:28],EXE_Instr[25:0],2'b00};
                Prediction_Success = (EXE_PredictResult.Target=={EXE_PC[31:28],EXE_Instr[25:0],2'b00});

                Branch_IsTaken = 1'b1;
                if(EXE_Instr[31:26] == `jal)
                    Branch_Type = `BranchIsCall;
                else 
                    Branch_Type = `BranchIsJump;
            end
        end
        else if(EXE_IsBranch)begin
            if(EXE_Instr[31:26] == `beq)begin
                if(EXE_Rs_Data == EXE_Rt_Data)begin
                   Branch_Target = EXE_PC + 4 + {EXE_Imm_32[29:0],2'b0};
                   Prediction_Success = (EXE_PredictResult.Target==EXE_PC + 4 + {EXE_Imm_32[29:0],2'b0});

                   Branch_IsTaken = 1'b1;
                   Branch_Type = `BranchIsBranch;
                end
                else begin
                    Branch_Target = EXE_PC + 8;
                    Prediction_Success =(EXE_PredictResult.Target==EXE_PC + 8);

                    Branch_IsTaken = 1'b0;
                    Branch_Type = `BranchIsBranch;
                end
            end
            else if(EXE_Instr[31:26] == `bne)begin
                 if(EXE_Rs_Data == EXE_Rt_Data)begin
                    Branch_Target = EXE_PC + 8;
                    Prediction_Success =(EXE_PredictResult.Target==EXE_PC + 8);

                    Branch_IsTaken = 1'b0;
                    Branch_Type = `BranchIsBranch;
                 end
                else begin
                   Branch_Target = EXE_PC + 4 + {EXE_Imm_32[29:0],2'b0};
                   Prediction_Success =(EXE_PredictResult.Target==EXE_PC + 4 + {EXE_Imm_32[29:0],2'b0});

                   Branch_IsTaken = 1'b1;
                   Branch_Type = `BranchIsBranch;
                end
            end
            else if(EXE_Instr[31:26] == `b_special)begin
                if(EXE_Instr[20:16] == `bgez || EXE_Instr[20:16] == `bgezal)begin
                    if(!EXE_Rs_Data[31])begin
                        Branch_Target = EXE_PC + 4 + {EXE_Imm_32[29:0],2'b0};
                        Prediction_Success =(EXE_PredictResult.Target==EXE_PC + 4 + {EXE_Imm_32[29:0],2'b0});

                        Branch_IsTaken = 1'b1;
                        Branch_Type = `BranchIsBranch;
                    end
                    else begin
                        Branch_Target = EXE_PC + 8;
                        Prediction_Success =(EXE_PredictResult.Target==EXE_PC + 8);

                        Branch_IsTaken = 1'b0;
                        Branch_Type = `BranchIsBranch;
                    end
                end
                else if(EXE_Instr[20:16] == `bltz || EXE_Instr[20:16] == `bltzal)begin
                    if(EXE_Rs_Data[31])begin
                        Branch_Target = EXE_PC + 4 + {EXE_Imm_32[29:0],2'b0};
                        Prediction_Success =(EXE_PredictResult.Target==EXE_PC + 4 + {EXE_Imm_32[29:0],2'b0});

                        Branch_IsTaken = 1'b1;
                        Branch_Type = `BranchIsBranch;
                    end
                    else begin
                        Branch_Target = EXE_PC + 8;
                        Prediction_Success =(EXE_PredictResult.Target==EXE_PC + 8);

                        Branch_IsTaken = 1'b0;
                        Branch_Type = `BranchIsBranch;
                    end
                end else begin
                    Branch_Target = EXE_PC + 8;
                    Prediction_Success =(EXE_PredictResult.Target==EXE_PC + 8);

                    Branch_IsTaken = 1'b0;
                    Branch_Type = `BranchIsBranch;
                end
            end
            else if(EXE_Instr[31:26] == `bgtz) begin
                if(!EXE_Rs_Data[31] && EXE_Rs_Data!=32'b0) begin
                    Branch_Target = EXE_PC + 4 + {EXE_Imm_32[29:0],2'b0};
                    Prediction_Success =(EXE_PredictResult.Target==EXE_PC + 4 + {EXE_Imm_32[29:0],2'b0});

                    Branch_IsTaken = 1'b1;
                    Branch_Type = `BranchIsBranch;
                end
                else begin
                    Branch_Target = EXE_PC + 8;
                    Prediction_Success =(EXE_PredictResult.Target==EXE_PC + 8);

                    Branch_IsTaken = 1'b0;
                    Branch_Type = `BranchIsBranch;
                end
            end
            else if(EXE_Instr[31:26] == `blez) begin
                if(EXE_Rs_Data[31]||EXE_Rs_Data==32'b0) begin
                    Branch_Target = EXE_PC + 4 + {EXE_Imm_32[29:0],2'b0};
                    Prediction_Success =(EXE_PredictResult.Target==EXE_PC + 4 + {EXE_Imm_32[29:0],2'b0});

                    Branch_IsTaken = 1'b1;
                    Branch_Type = `BranchIsBranch;
                end
                else begin
                    Branch_Target = EXE_PC + 8;
                    Prediction_Success =(EXE_PredictResult.Target==EXE_PC + 8);

                    Branch_IsTaken = 1'b0;
                    Branch_Type = `BranchIsBranch;
                end
            end
            else begin
                Branch_Target = EXE_PC + 8;
                Prediction_Success = 1'b0;

                Branch_IsTaken = 1'b0;
                Branch_Type = `BranchIsNone;
                // Actually this won't happen, but for code style restriction
            end
        end
        else begin
            Branch_Target = EXE_PC + 8;
            Prediction_Success = 1'b0;

            Branch_IsTaken = 1'b0;
            Branch_Type = `BranchIsNone;
        end
    end
    
    assign EXE_FactResult.Type      = Branch_Type;
    assign EXE_FactResult.IsTaken   = Branch_IsTaken;
    assign EXE_FactResult.Target    = Branch_Target;
    assign EXE_FactResult.PC        = EXE_PC;
    assign EXE_FactResult.Count     = EXE_PredictResult.Count;
    assign EXE_FactResult.Hit       = EXE_PredictResult.Hit;
    assign EXE_FactResult.Valid     = EXE_PredictResult.Valid && !ID_EXE_Stall && (Branch_Type != `BranchIsNone);


    assign EXE_Correction_Vector    = (EXE_PF_FlushAll)?EXE_PC+4:Branch_Target;
    assign EXE_IsTaken              = Branch_IsTaken;

    assign EXE_Prediction_Failed    = ~Prediction_Success&&(Branch_Type != `BranchIsNone);
    assign EXE_PF_FlushAll          = (Branch_Type == `BranchIsNone) && EXE_PredictResult.Valid;

logic [31:0]success_count;
logic [31:0]sum_count;
always_ff @(posedge clk,negedge rst)begin
    if(!rst)begin
        success_count = '0;
        sum_count = '0;
    end
    else if(Branch_Type != `BranchIsNone)begin
        sum_count = sum_count + 1;
        if(Prediction_Success)
        success_count = success_count + 1;
    end
    
end
endmodule
